package com.bishe.util;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @description: HttpRequestUtils
 * @author: wwz
 * @create: 2023-11-30 17:48
 **/

public class HttpRequestUtils {

    private static final String ENCODING = "UTF-8";
    private static final int BUFFER_SIZE = 1024;

    /**
     * 定义分页参数在HTTP请求参数中KEY的名称
     */

    private static final String PAGE_INDEX_NAME = "pageindex";
    private static final String PAGE_SIZE_NAME = "pagesize";
    private static final String SORT_FIELD_NAME = "sortfields";
    /**
     * /1代表升序，0代表降序
     */

    private static final String SORT_TYPE_NAME = "sorttypes";
    private static final String LEFTBRACKETS_MIDDLE = "[";
    private static final String LEFTBRACKETS_BIG = "{";
    private static final String XMLPREFFIX = "<?xml";
    private static final String UNKNOWN = "unknown";

    private static List<Pattern> patterns = null;
    private static List<Pattern> SQLpatterns = null;
    private static final Logger logger = LogManager.getLogger(HttpRequestUtils.class);
    /**
     * 查询字段中的系统字段
     */
    public static List<String> getSystem_Keys() {
        List<String> systemQueryFlds = new ArrayList<String>();
        systemQueryFlds.add(PAGE_INDEX_NAME);
        systemQueryFlds.add(PAGE_SIZE_NAME);
//    	systemQueryFlds.add(RESULT_FLdS_NAME);
        systemQueryFlds.add(SORT_FIELD_NAME);
        systemQueryFlds.add(SORT_TYPE_NAME);
        //添加客户端查询中带有的字段，这些字段都过滤掉作为sql查询的过滤字段
        systemQueryFlds.add("t");
        systemQueryFlds.add("random");
        return systemQueryFlds;
    }


    /**
     * 从HttpServletRequest对象中获取Post发送的字符串
     *
     * @throws IOException
     */
    public static String getDataFromRequest(HttpServletRequest request) throws IOException {
        //获取请求流,并转为字符串,使用DOM4J解析该XML
        ServletInputStream postQueryStream = request.getInputStream();
        byte[] buffer = new byte[BUFFER_SIZE];
        int length = 0;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        do {
            length = postQueryStream.read(buffer);
            if (length > 0) {
                baos.write(buffer, 0, length);
            }
        } while (length > 0);

        String strQueryText = new String(baos.toByteArray(), ENCODING);
        //xss注入和sql注入
        if (strQueryText.startsWith(LEFTBRACKETS_BIG) || strQueryText.startsWith(LEFTBRACKETS_MIDDLE)) {
            strQueryText = stripJsonXss(strQueryText);
            strQueryText = stripJsonSQL(strQueryText);
        } else if (strQueryText.startsWith(XMLPREFFIX)) {
            strQueryText = stripXmlXss(strQueryText);
            strQueryText = stripXmlSQL(strQueryText);
        }
        return strQueryText;
    }

    private static List<Object[]> getXssPatternList() {
        List<Object[]> ret = new ArrayList<Object[]>();
        ret.add(new Object[]{"<(no)?script[^>]*>.*?</(no)?script>", Pattern.CASE_INSENSITIVE});
        ret.add(new Object[]{"eval\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
        ret.add(new Object[]{"expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
        ret.add(new Object[]{"(javascript:|vbscript:|view-source:)*", Pattern.CASE_INSENSITIVE});
        ret.add(new Object[]{"(window\\.location|window\\.|\\.location|document\\.cookie|document\\.|alert\\(.*?\\)|alert.\\(.*?\\)|window\\.open\\()*", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL});
        ret.add(new Object[]{"<+\\s*\\w*\\s*(oncontrolselect|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|function|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror=|onerroupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmouseout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onabort|onactivate|onafterprint|onafterupdate|onbefore|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload)+\\s*=+", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL|Pattern.CASE_INSENSITIVE});
        ret.add(new Object[]{"\\s*\\w*\\s*(oncontrolselect|object data|oncopy|oncut|ondataavailable|ondatasetchanged|ondatasetcomplete|ondblclick|ondeactivate|ondrag|ondragend|function|ondragenter|ondragleave|ondragover|ondragstart|ondrop|onerror=|onerroupdate|onfilterchange|onfinish|onfocus|onfocusin|onfocusout|onhelp|onkeydown|onkeypress|onkeyup|onlayoutcomplete|onload|onlosecapture|onmousedown|onmouseenter|onmouseleave|onmousemove|onmouseout|onmouseover|onmouseup|onmousewheel|onmove|onmoveend|onmovestart|onabort|onactivate|onafterprint|onafterupdate|onbefore|onbeforeactivate|onbeforecopy|onbeforecut|onbeforedeactivate|onbeforeeditocus|onbeforepaste|onbeforeprint|onbeforeunload|onbeforeupdate|onblur|onbounce|oncellchange|onchange|onclick|oncontextmenu|onpaste|onpropertychange|onreadystatechange|onreset|onresize|onresizend|onresizestart|onrowenter|onrowexit|onrowsdelete|onrowsinserted|onscroll|onselect|onselectionchange|onselectstart|onstart|onstop|onsubmit|onunload)+\\s*=+", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL|Pattern.CASE_INSENSITIVE});
        return ret;
    }
    private static List<Object[]> getSQLPatternList() {
        List<Object[]> ret = new ArrayList<Object[]>();
        /*ret.add(new Object[]{"(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute|like|in)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL|Pattern.CASE_INSENSITIVE});*/
        ret.add(new Object[]{"(\\bselect\\b |\\bselect*\\b|\\bselect\\(|\\bupdate\\b |\\bdelete\\b |\\binsert\\b |\\btrancate\\b |\\bascii\\b |\\bdeclare\\b |\\bexec\\b |\\bmaster\\b |\\bdrop\\b |\\bexecute\\b |\\bpg_sleep\\b |\\bpg_sleep\\(|\\bversion\\b |\\bversion\\(|\\bgetpgusername\\b|\\bcurrent_database\\b|\\bpg_read_file\\b|\\bsystem\\b |\\bsystem\\(|\\bcopy\\b|\\bsys.v_\\$version |\\bget_host_name\\b|\\bget_host_name\\(|\\bdrithsx.sn\\b|\\bXMLType\\b|\\bcheckin\\b|\\bmakeversioned\\b|\\buncheckout\\b|\\bsqlid_to_sqlhash\\b|\\bgetmappingxpath\\b|\\bdecode\\b|\\bget_host_address\\b|\\bRECEIVE_MESSAGE)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL|Pattern.CASE_INSENSITIVE});
        return ret;

    }

    private static List<Pattern> getSQLPatterns() {
        SQLpatterns = null;

        List<Pattern> list = new ArrayList<Pattern>();
        String regex = null;
        Integer flag = null;
        int arrLength = 0;
        for (Object[] arr : getSQLPatternList()) {
            arrLength = arr.length;
            for (int i = 0; i < arrLength; i++) {
                regex = (String) arr[0];
                flag = (Integer) arr[1];
                list.add(Pattern.compile(regex, flag));
            }
        }
        SQLpatterns = list;

        return SQLpatterns;
    }
    private static List<Pattern> getPatterns() {
        if (patterns == null) {
            List<Pattern> list = new ArrayList<Pattern>();
            String regex = null;
            Integer flag = null;
            int arrLength = 0;
            for (Object[] arr : getXssPatternList()) {
                arrLength = arr.length;
                for (int i = 0; i < arrLength; i++) {
                    regex = (String) arr[0];
                    flag = (Integer) arr[1];
                    list.add(Pattern.compile(regex, flag));
                }
            }
            patterns = list;
        }
        return patterns;
    }

    public static String stripJsonXss(String value) throws IOException {
        return stripXss(value,true);
    }

    public static String stripXmlXss(String value) throws IOException {
        return stripXss(value,false);
    }
    public static String stripJsonSQL(String value) throws IOException {
        return stripSQL(value,true);
    }

    public static String stripXmlSQL(String value) throws IOException {
        return stripSQL(value,false);
    }
    private static String stripSQL(String value, boolean replace) throws IOException {
        if (value != null && !"".equals(value)) {
            Matcher matcher = null;
            for (Pattern pattern : getSQLPatterns()) {
                matcher = pattern.matcher(value);
                if (matcher.find()) {
                    String afterRep = matcher.replaceAll("");
                    if (!value.equals(afterRep)) {
                        logger.error("包含系统敏感词！疑似SQL注入:"+"疑似sql注入参数:"+value+" "+"正则替换后的参数:"+afterRep);
                        throw new IOException("包含系统敏感词！疑似SQL注入");
                    }
                }
            }
            if (replace) {
                value = value.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
            }
        }
        return value;
    }


    private static String stripXss(String value, boolean replace) throws IOException {
        if (value != null && !"".equals(value)) {
            Matcher matcher = null;
            for (Pattern pattern : getPatterns()) {
                matcher = pattern.matcher(value);
                if (matcher.find()) {
                    String afterRep = matcher.replaceAll("");
                    if (!value.equals(afterRep)) {
                        throw new IOException("包含系统敏感词！疑似XSS注入");
                    }
                }
            }
            if (replace) {
                value = value.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
            }
        }
        return value;
    }

}
